Skip to content

feat(http-utils): fan out payload scopes[] into AuthInfo with namespace + reserved-name guards (SITES-46454)#1676

Open
ravverma wants to merge 3 commits into
mainfrom
feat/jwt-scopes-fanout
Open

feat(http-utils): fan out payload scopes[] into AuthInfo with namespace + reserved-name guards (SITES-46454)#1676
ravverma wants to merge 3 commits into
mainfrom
feat/jwt-scopes-fanout

Conversation

@ravverma

@ravverma ravverma commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Phase 1 of multi-product login support (SITES-46453). Extends JwtHandler.checkAuth to map payload-side scopes[] strings (minted by spacecat-auth-service for allow-listed IMS client_ids) into first-class AuthInfo scope objects, so consumers can authorise via authInfo.hasScope(...) / checkScopes(...) — the same surface used today for admin / read_only_admin / per-tenant scopes.

Companion PRs:

  • spacecat-auth-service — mints the scope (PR)
  • spacecat-api-service — consumes the scope on GET /organizations/{id}/sites (PR; waits on this release)

Design ADR: adobe/mysticat-architecture#139
Jira: SITES-46454 (parent: SITES-46453)

Discipline (the JWT signature already prevents forgery — this is shape hygiene)

  • ALLOWED_PAYLOAD_SCOPE_NAMES — canonical allow-list. Entries outside it are dropped and warn-logged. Starts with sites:list:cross_product (the only Phase 1 scope). Grows by ADR amendment only.
  • RESERVED_SCOPE_NAMES (admin / read_only_admin / user) — NEVER sourced from payload.scopes[]. Those scopes come exclusively from the dedicated boolean / tenant claims. Prevents an accidental future mint from synthesising a privileged scope.
  • Non-string entries are dropped defensively.

Both constants are exported as the single source of truth shared with the minter.

Test plan

  • npm test -w packages/spacecat-shared-http-utils — 463 passing, 100% lines/statements coverage
  • Lint clean
  • Merge first so spacecat-shared-http-utils gets a release the api-service can pick up

🤖 Generated with Claude Code

…ce + reserved-name guards (SITES-46454)

Phase 1 of multi-product login support. Extends JwtHandler.checkAuth
to map payload-side scopes[] strings (minted by spacecat-auth-service
for allow-listed IMS client_ids) into first-class AuthInfo scope
objects, so consumers can authorise via authInfo.hasScope(...) and
checkScopes(...) — the same surface used today for admin /
read_only_admin / per-tenant scopes.

Discipline (the JWT signature already prevents forgery — this is
shape hygiene to keep the minter and consumer well-defined):

  - ALLOWED_PAYLOAD_SCOPE_NAMES is the canonical allow-list; entries
    outside it are dropped and logged at warn-level. Starts with
    sites:list:cross_product (the only Phase 1 scope).
  - RESERVED_SCOPE_NAMES (admin / read_only_admin / user) are NEVER
    sourced from payload.scopes[] — those scopes come exclusively
    from the dedicated boolean / tenant claims. This prevents an
    accidental future mint from synthesising a privileged scope.
  - Non-string entries are dropped defensively.

Both constants are exported as the single source of truth shared
with the minter (spacecat-auth-service login.js).

Design ADR: adobe/mysticat-architecture#139
Jira: SITES-46454

Tests:
- allow-listed scope flows into AuthInfo.hasScope
- unknown scopes are dropped and warn-logged
- reserved names (admin, read_only_admin, user) cannot be elevated
  via payload.scopes[]
- missing/empty scopes claim is a no-op
- non-string entries are dropped defensively

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ravverma ravverma requested a review from MysticatBot June 15, 2026 08:39
@github-actions

Copy link
Copy Markdown

This PR will trigger a minor release when merged.

@ravverma ravverma marked this pull request as ready for review June 15, 2026 08:39
@ravverma ravverma requested review from MysticatBot and removed request for MysticatBot June 30, 2026 09:47

@MysticatBot MysticatBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ravverma,

Verdict: Approve - well-structured security feature with sound allow-list and reserved-name guards.
Complexity: HIGH - medium diff; auth signal.
Changes: Extends JwtHandler.checkAuth to fan out payload-side scopes[] into first-class AuthInfo scope objects with allow-list gating and reserved-name exclusion (2 files).

Non-blocking (4): minor issues and suggestions
  • nit: Double [jwt] prefix in log message - the new this.log('[jwt] ignoring unknown payload scope...') call produces [jwt] [jwt] ignoring... because AbstractHandler.log already prepends [jwt]. Drop the manual prefix to match the intended output. Consistent with the existing S2S log on line 63 (pre-existing), so non-blocking. - packages/spacecat-shared-http-utils/src/auth/handlers/jwt.js:108
  • suggestion: Use Set instead of Array for ALLOWED_PAYLOAD_SCOPE_NAMES and RESERVED_SCOPE_NAMES - communicates membership-test intent and gives O(1) lookup when the allow-list grows per the ADR amendment process. - packages/spacecat-shared-http-utils/src/auth/handlers/jwt.js:44-48
  • suggestion: Add a test for non-array truthy payload.scopes values (e.g., a string or object) to confirm the Array.isArray guard handles them silently. - packages/spacecat-shared-http-utils/test/auth/handlers/jwt.test.js
  • suggestion: Consider deduplicating payload scopes before pushing to prevent duplicate { name } entries when the same scope string appears more than once in payload.scopes[]. - packages/spacecat-shared-http-utils/src/auth/handlers/jwt.js:106

Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 1m 56s | Cost: $4.60 | Commit: 95452bac60de906fcdc4bafe1c525c5e35f2b821
If this code review was useful, please react with 👍. Otherwise, react with 👎.

@MysticatBot MysticatBot added ai-reviewed Reviewed by AI complexity:high High complexity PR labels Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-reviewed Reviewed by AI complexity:high High complexity PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants